package com.example.timing_wheel.tools;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;

@Data
@Slf4j
public class TimerLauncher {

    // 底层时间轮
    private TimeWheel timeWheel;

    // 一个 Timer 只有一个延时队列
    private DelayQueue<TimerTaskList> delayQueue = new DelayQueue<>();

    // 过期任务执行线程
    private ExecutorService workerThreadPool;

    // 轮询 delayQueue 获取过期任务线程
    private ScheduledExecutorService bossThreadPool;


    public TimerLauncher() {
        this.timeWheel = new TimeWheel(1, 20, System.currentTimeMillis(), delayQueue);
        this.workerThreadPool = Executors.newFixedThreadPool(100);
        this.bossThreadPool = Executors.newScheduledThreadPool(1); // 使用 ScheduledExecutorService
    }


    private void addTimerTaskEntry(TimerTaskEntry entry) {
        if (!timeWheel.add(entry)) {
            // 任务已到期
            TimerTask timerTask = entry.getTimerTask();
            log.info("=====任务:{} 已到期,准备执行============", timerTask.getDesc());
            workerThreadPool.submit(timerTask);
        }
    }

    public void add(TimerTask timerTask) {
        log.info("=======添加任务开始====task:{}", timerTask.getDesc());
        TimerTaskEntry entry = new TimerTaskEntry(timerTask, timerTask.getDelayMs() + System.currentTimeMillis());
        timerTask.setTimerTaskEntry(entry);
        addTimerTaskEntry(entry);
    }

    /**
     * 推动指针运转获取过期任务
     *
     * @param timeout 时间间隔
     */
    private synchronized void advanceClock(long timeout) {
        try {
            TimerTaskList bucket = delayQueue.poll(timeout, TimeUnit.MILLISECONDS);
            if (bucket != null) {
                // 推进时间
                timeWheel.advanceCLock(bucket.getExpiration());
                // 执行过期任务(包含降级)
                bucket.clear(this::addTimerTaskEntry);
            }
        } catch (InterruptedException e) {
            log.error("advanceClock error");
        }
    }

    // 启动定时器
    public void start() {
        // 每 20ms 推动一次时间轮运转
        this.bossThreadPool.scheduleAtFixedRate(() -> {
            this.advanceClock(20);
        }, 0, 20, TimeUnit.MILLISECONDS);
    }

    // 关闭定时器
    public void shutdown() {
        this.bossThreadPool.shutdown();
        this.workerThreadPool.shutdown();
        this.timeWheel = null;
    }
}
